package net.randomice.emf.observables.demo.extlibrary.rcp.editors;

import java.util.Date;

import net.randomice.emf.observables.demo.extlibrary.ExtlibraryPackage;
import net.randomice.emf.observables.demo.extlibrary.items.BookCategory;
import net.randomice.emf.observables.demo.extlibrary.items.ItemsPackage;
import net.randomice.emf.observables.demo.extlibrary.items.observables.BookOO;
import net.randomice.emf.observables.demo.extlibrary.observables.LibraryOO;
import net.randomice.emf.observables.demo.extlibrary.observables.LibraryOS;
import net.randomice.emf.observables.demo.extlibrary.persons.PersonsPackage;
import net.randomice.emf.observables.demo.extlibrary.persons.observables.WriterOL;
import net.randomice.emf.observables.demo.extlibrary.persons.observables.WriterOO;
import net.randomice.emf.observables.runtime.IObservableListObject;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.emf.databinding.EMFObservables;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;

public class EMFObservablesDatabindingPage extends FormPage {
	private LibraryOO myFlat;
	private LibraryOO currentLibrary;
	private WriterOO currentWriter;
	private BookOO currentBook;
	private LibraryOO world;
	private WriterOL writers;

	public EMFObservablesDatabindingPage(FormEditor editor) {
		super(editor, "emf.observables", "emf.observables Databinding");
		initData();
	}

	@SuppressWarnings("deprecation")
	private void initData() {
		world = LibraryOO.createRoot();

		myFlat = world.getBranches().addNew();
		myFlat.setName("Enrico's flat");
		myFlat.setAddress("Leipzig");

		LibraryOO bc1 = myFlat.getBranches().addNew();
		bc1.setName("book case 1");
		bc1.setAddress("living room");

		LibraryOO bc2 = myFlat.getBranches().addNew();
		bc2.setName("book case 2");
		bc2.setAddress("working room");

		WriterOO daveSteinberg = world.getWriters().addNew();
		daveSteinberg.setFirstName("Dave");
		daveSteinberg.setLastName("Steinberg");

		WriterOO unknown = world.getWriters().addNew();
		unknown.setFirstName("Un");
		unknown.setLastName("Known");

		WriterOO mankell = world.getWriters().addNew();
		mankell.setFirstName("Henning");
		mankell.setLastName("Mankell");

		BookOO dancingMaster = bc1.getBooks().addNew();
		dancingMaster.setTitle("The Return of the Dancing Master");
		dancingMaster.setAuthor(mankell.getValue());
		dancingMaster.setCategory(BookCategory.MYSTERY);
		dancingMaster.setPages(520);

		BookOO emfBook2Ed = bc2.getBooks().addNew();
		emfBook2Ed.setTitle("Eclipse Modeling Framework - 2nd Ed.");
		emfBook2Ed.setCategory(BookCategory.REFERENCE);
		emfBook2Ed.setPages(704);
		emfBook2Ed.setPublicationDate(new Date(2008, 12, 1));
		emfBook2Ed.setAuthor(daveSteinberg.getValue());

		BookOO tIBoI = myFlat.getBooks().addNew();
		tIBoI.setTitle("The Invisible Book of Invisibility");
		tIBoI.setAuthor(unknown.getValue());
		tIBoI.setCategory(BookCategory.SCIENCE_FICTION);
		tIBoI.setPages(567);

		writers = world.getWriters();
	}

	@Override
	protected void createFormContent(IManagedForm managedForm) {
		super.createFormContent(managedForm);
		ScrolledForm form = managedForm.getForm();
		FormToolkit toolkit = managedForm.getToolkit();
		Composite formBody = form.getBody();

		form.setText("emf.observables Databinding");

		formBody.setLayout(new GridLayout(3, false));

		DataBindingContext dbc = new DataBindingContext();

		createLibrariesSection(toolkit, formBody, dbc);
		createLibrarySection(toolkit, formBody, dbc);
		createBooksSection(toolkit, formBody, dbc);
		createBookSection(toolkit, formBody, dbc);
		createWritersSection(toolkit, formBody, dbc);
		createWriterSection2(toolkit, formBody, dbc);

	}

	private void createLibrariesSection(FormToolkit toolkit,
			Composite formBody, DataBindingContext dbc) {
		Composite client = createSection(toolkit, formBody, "Libraries",
				new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
		client.setLayout(new GridLayout(2, false));

		final TreeViewer treeViewer = new TreeViewer(toolkit.createTree(client,
				SWT.NONE));
		treeViewer.getTree().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
		ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(
				EMFObservables.listFactory(Realm.getDefault(),
						ExtlibraryPackage.Literals.LIBRARY__BRANCHES), null);
		treeViewer.setLabelProvider(new ObservableMapLabelProvider(
				new LibraryOS(contentProvider.getKnownElements()).getName()));
		treeViewer.setContentProvider(contentProvider);
		treeViewer.setInput(world.getValue());
		treeViewer.setAutoExpandLevel(3);
		toolkit.createButton(client, "+", SWT.NONE).addSelectionListener(
				new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						LibraryOO object = currentLibrary.getBranches()
								.addNew();
						treeViewer.setSelection(new StructuredSelection(object
								.getValue()), true);
						treeViewer.getTree().setFocus();
					}
				});

		IObservableValue selected = ViewersObservables
				.observeSingleSelection(treeViewer);
		currentLibrary = new LibraryOO(selected);
		currentLibrary.setValue(myFlat.getValue());
		currentLibrary.o.addValueChangeListener(new IValueChangeListener() {
			public void handleValueChange(ValueChangeEvent event) {
				currentBook.setValue(null);
			}
		});
	}

	private void createLibrarySection(FormToolkit toolkit, Composite formBody,
			DataBindingContext dbc) {
		Composite client = createSection(toolkit, formBody, "Library",
				new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
		client.setLayout(new GridLayout(2, false));

		createText(toolkit, dbc, client, "Name", currentLibrary.getName());

		createText(toolkit, dbc, client, "Address", currentLibrary.getAddress());
	}

	private void createBooksSection(FormToolkit toolkit, Composite formBody,
			DataBindingContext dbc) {
		Composite client = createSection(toolkit, formBody, "Books",
				new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		client.setLayout(new GridLayout(2, false));

		IObservableValue selected = createTable(toolkit, client, currentLibrary
				.getBooks(), ItemsPackage.Literals.BOOK__TITLE);
		currentBook = new BookOO(selected);
	}

	private void createBookSection(FormToolkit toolkit, Composite formBody,
			DataBindingContext dbc) {
		Composite client = createSection(toolkit, formBody, "Book",
				new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		client.setLayout(new GridLayout(2, false));

		createText(toolkit, dbc, client, "Title", currentBook.getTitle());

		toolkit.createLabel(client, "Category");
		ComboViewer combo = new ComboViewer(new Combo(client, SWT.NONE));
		combo.getControl().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, false));
		combo.setContentProvider(new ArrayContentProvider());
		combo.setLabelProvider(new LabelProvider());
		combo.setInput(BookCategory.values());
		dbc.bindValue(ViewersObservables.observeSingleSelection(combo),
				currentBook.getCategory());

		final Spinner pages = createSpinner(toolkit, dbc, client, "Pages",
				currentBook.getPages());

		toolkit.createLabel(client, "Author");
		ComboViewer author = new ComboViewer(client, SWT.NONE);
		author.getControl().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, false));
		author.setLabelProvider(new ObservableMapLabelProvider(writers
				.getName()));
		author.setContentProvider(new ObservableListContentProvider());
		author.setInput(writers.getList());
		dbc.bindValue(ViewersObservables.observeSingleSelection(author),
				currentBook.getAuthor().o);

		currentBook.o.addValueChangeListener(new IValueChangeListener() {
			public void handleValueChange(ValueChangeEvent event) {
				if (null == event.diff.getNewValue()) {
					pages.setSelection(0);
				}
			}
		});
	}

	private void createWritersSection(FormToolkit toolkit, Composite formBody,
			DataBindingContext dbc) {
		Composite client = createSection(toolkit, formBody, "Writers",
				new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		client.setLayout(new GridLayout(2, false));

		IObservableValue selected = createTable(toolkit, client, writers,
				PersonsPackage.Literals.WRITER__NAME);
		currentWriter = new WriterOO(selected);
	}

	private void createWriterSection2(FormToolkit toolkit, Composite formBody,
			DataBindingContext dbc) {
		Composite client = createSection(toolkit, formBody, "Writer",
				new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
		client.setLayout(new GridLayout(2, false));

		createText(toolkit, dbc, client, "First name", currentWriter
				.getFirstName());

		createText(toolkit, dbc, client, "Last name", currentWriter
				.getLastName());
	}

	private Composite createSection(FormToolkit toolkit, Composite formBody,
			String title, GridData gridData) {
		Section section = toolkit.createSection(formBody, Section.TITLE_BAR);
		section.setLayoutData(gridData);
		Composite client = toolkit.createComposite(section);
		section.setClient(client);
		section.setText(title);
		return client;
	}

	private IObservableValue createTable(FormToolkit toolkit, Composite client,
			final IObservableListObject<?> list, EAttribute titleAttribute) {
		final TableViewer tableViewer = new TableViewer(toolkit.createTable(
				client, SWT.BORDER));
		tableViewer.getTable().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
		ObservableListContentProvider contentProvider = new ObservableListContentProvider();
		tableViewer.setLabelProvider(new ObservableMapLabelProvider(
				EMFObservables.observeMap(contentProvider.getKnownElements(),
						titleAttribute)));
		tableViewer.setContentProvider(contentProvider);
		tableViewer.setInput(list.getList());
		toolkit.createButton(client, "+", SWT.NONE).addSelectionListener(
				new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						Object obj = list.addNew();
						tableViewer.setSelection(new StructuredSelection(obj),
								true);
						tableViewer.getTable().setFocus();
					}
				});

		return ViewersObservables.observeSingleSelection(tableViewer);
	}

	private void createText(FormToolkit toolkit, DataBindingContext dbc,
			Composite client, String title, IObservableValue value) {
		toolkit.createLabel(client, title);
		Text text = toolkit.createText(client, "");
		text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		dbc.bindValue(SWTObservables.observeText(text, SWT.Modify), value);
	}

	private Spinner createSpinner(FormToolkit toolkit, DataBindingContext dbc,
			Composite client, String title, IObservableValue value) {
		toolkit.createLabel(client, title);
		Spinner spinner = new Spinner(client, SWT.NONE);
		spinner.setMaximum(5000);
		spinner.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		dbc.bindValue(SWTObservables.observeSelection(spinner), value);
		return spinner;
	}
}
